package utils

import (
	"errors"
	"io"
	"log/slog"
	"mime/multipart"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/gin-gonic/gin/binding"
)

func GetRequestQueryMap(c *gin.Context) map[string]string {
	var valueMap = make(map[string]string)
	values := c.Request.URL.Query()
	for key, value := range values {
		if len(value) > 0 {
			valueMap[key] = value[0]
		}
	}
	return valueMap
}

func GetRequestFormMap(c *gin.Context) map[string]string {
	var valueMap = make(map[string]string)
	err := c.Request.ParseForm()
	if err != nil {
		slog.Error(err.Error())
	}
	values := c.Request.PostForm
	for key, value := range values {
		if len(key) > 0 && len(value) > 0 {
			valueMap[key] = value[0]
		}
	}
	return valueMap
}

func GetRequestBodyData(c *gin.Context) string {
	if strings.Contains(c.GetHeader("Content-Type"), "multipart/form-data") {
		return "[binary data]"
	} else {
		// 获取body内容
		data, err := GetRequestBodyBytes(c)
		if err != nil {
			slog.Error(err.Error())
			return ""
		} else {
			return string(data)
		}
	}
}

func GetRequestBodyBytes(c *gin.Context) ([]byte, error) {
	if cb, ok := c.Get(gin.BodyBytesKey); ok {
		if cbb, ok := cb.([]byte); ok {
			return cbb, nil
		} else {
			return nil, errors.New("cannot convert request body content to byte array")
		}
	} else {
		body, err := io.ReadAll(c.Request.Body)
		if err != nil {
			return nil, err
		}
		c.Set(gin.BodyBytesKey, body)
		return body, nil
	}
}

func ShouldBindJSON(c *gin.Context, obj any) error {
	return c.ShouldBindBodyWith(obj, binding.JSON)
}

func BindJSON(c *gin.Context, obj any) error {
	if err := c.ShouldBindBodyWith(obj, binding.JSON); err != nil {
		slog.Error(err.Error())
		if err2 := c.AbortWithError(http.StatusBadRequest, err).SetType(gin.ErrorTypeBind); err != nil {
			slog.Error(err2.Error())
		}
		return err
	}
	return nil
}

func SaveUploadedTempFile(c *gin.Context, file *multipart.FileHeader) (string, error) {
	var fileName = file.Filename

	var suffix = ""
	if strings.Index(fileName, ".") >= 0 {
		suffix = strings.ToLower(fileName[strings.LastIndex(fileName, "."):])
	}
	suffix = strings.ToLower(suffix)

	var fileDir = "temp/" + time.Now().Format(TimeFormatDateShort)
	if _, err := os.Stat(fileDir); err != nil {
		if err := os.MkdirAll(fileDir, os.ModePerm); err != nil {
			slog.Error(err.Error())
			return "", errors.New("新建临时文件夹失败")
		}
	}

	var filePath = fileDir + "/" + strconv.FormatInt(time.Now().UnixNano(), 10) + suffix
	if err := c.SaveUploadedFile(file, filePath); err != nil {
		// 保存图片失败
		slog.Error(err.Error())
		return "", errors.New("保存临时图片失败")
	}

	return filePath, nil
}

func GetQueryIdArr(c *gin.Context) []int64 {
	var idArr = make([]int64, 0)
	id := c.Query("id")
	if len(id) > 0 {
		for _, idStr := range strings.Split(id, ",") {
			idInt, err := strconv.ParseInt(idStr, 10, 64)
			if err != nil {
				slog.Error(err.Error())
				continue
			} else {
				idArr = append(idArr, idInt)
			}
		}
	}
	return idArr
}

func GetSortType(c *gin.Context, defaultSorts ...string) string {
	var sortType = c.Query("sortType")
	if len(sortType) > 0 {
		sortType = strings.ReplaceAll(sortType, "-", ", ")
		sortType = strings.ReplaceAll(sortType, ".ascend", " asc")
		sortType = strings.ReplaceAll(sortType, ".descend", " desc")
		sortType = ToSnakeCase(sortType)
		return sortType
	} else if len(defaultSorts) > 0 {
		return ArrayToString(defaultSorts, ",")
	} else {
		return "id desc"
	}
}
